﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using System.Xml.Linq;
using King.Engine.Core;

namespace King.Engine.Content
{
    /// <summary>
    /// Static class that contains references to all loaded content
    /// </summary>
    public static class ContentLoader
    {

        #region Fields

        private static ContentManager _contentManager;
        private static Dictionary<string, object> _contentTable;

        #endregion


        #region Constants

        private const string TAG_TEXTURE = "Texture";
        private const string TAG_AUDIO = "Audio";
        private const string TAG_SPRITE_FONT = "SpriteFont";
        private const string TAG_LEVEL = "Level";

        #endregion


        #region Public Methods

        /// <summary>
        /// Get content of type T with the given name
        /// </summary>
        public static T Get<T>(string name)
        {
            return (T) _contentTable[name];
        }

        /// <summary>
        /// Load the level of the given name to the current activity. 
        /// All elements in the level's file will be loaded as constructors to the implied GameObject
        /// </summary>
        public static void LoadLevel(string levelName)
        {
            LoadLevel<Activity, DefaultLevelScheme<Activity>>(levelName);
        }

        /// <summary>
        /// Load the level of the given name to the current activity. 
        /// </summary>
        /// <typeparam name="ActivityType">The type of the current activity</typeparam>
        /// <typeparam name="LevelSchemeType">The type of LevelScheme that builds the level</typeparam>
        /// <param name="levelName">The name of the level</param>
        public static void LoadLevel<ActivityType,LevelSchemeType>(string levelName) where ActivityType:Activity 
                                                                                     where LevelSchemeType:LevelScheme<ActivityType>, new()
        {
            LevelSchemeType levelScheme = new LevelSchemeType();
            ActivityType activity = ActivityManager.CurrentActivity as ActivityType;

            XDocument levelFile = Get<XDocument>(levelName);
            XElement root = levelFile.Root;

            var elements = root.Elements();
            foreach (var element in elements)
            {
                string elementName = element.Name.ToString();
                var attributes = element.Attributes()
                                        .ToDictionary(a => a.Name.LocalName, a => a.Value);
                
                levelScheme[elementName](activity, attributes);
            }
        }

        #endregion


        #region Internal Methods

        internal static void Load(ContentManager contentManager, string contentTableFile)
        {
            _contentManager = contentManager;
            string[] contentLines;

            using (Stream stream = new FileStream(_contentManager.RootDirectory + "\\" + contentTableFile, FileMode.Open))
            {
                using (StreamReader strStream = new StreamReader(stream))
                {
                    contentLines = strStream.ReadToEnd().Split('\n');
                }
            }

            _contentTable = new Dictionary<string, object>();
            foreach (string contentLine in contentLines)
            {
                if (contentLine.Trim() == String.Empty) continue;

                string[] token = contentLine.Split(',');

                string contentType = token[0].Trim();
                string contentName = token[1].Trim();
                string contentPath = token[2].Trim();

                object content = LoadContent(contentType, contentPath);
                _contentTable.Add(contentName, content);
            }
        }

        #endregion


        #region Private Methods

        private static object LoadContent(string tag, string path)
        {
            switch (tag)
            {
                case TAG_TEXTURE:
                    return _contentManager.Load<Texture2D>(path);

                case TAG_SPRITE_FONT:
                    return _contentManager.Load<SpriteFont>(path);

                case TAG_AUDIO:
                    return _contentManager.Load<SoundEffect>(path);

                case TAG_LEVEL:
                    string fullPath = _contentManager.RootDirectory + @"\" + path;
                    return XDocument.Load(fullPath);

                default:
                    throw new InvalidDataException("Unrecognized content type: " + tag);
            }
        }

        #endregion

    }
}
